import os
import uuid
import aiofiles
from typing import Optional, Tuple
from fastapi import UploadFile, HTTPException
from PIL import Image
import io
from config import settings
import hashlib
from datetime import datetime

def generate_filename(original_filename: str, prefix: str = "") -> str:
    """
    生成唯一的文件名
    
    Args:
        original_filename: 原始文件名
        prefix: 文件名前缀
    
    Returns:
        str: 生成的唯一文件名
    """
    # 获取文件扩展名
    _, ext = os.path.splitext(original_filename)
    # 生成UUID作为文件名
    unique_id = str(uuid.uuid4())
    # 添加时间戳确保唯一性
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    
    if prefix:
        return f"{prefix}_{timestamp}_{unique_id}{ext}"
    else:
        return f"{timestamp}_{unique_id}{ext}"

def validate_file_type(file: UploadFile, allowed_types: str) -> bool:
    """
    验证文件类型
    
    Args:
        file: 上传的文件
        allowed_types: 允许的文件类型，逗号分隔
    
    Returns:
        bool: 文件类型是否有效
    """
    if not file.content_type:
        return False
    
    allowed_list = [t.strip() for t in allowed_types.split(",")]
    return file.content_type in allowed_list

def validate_file_size(file: UploadFile, max_size: int) -> bool:
    """
    验证文件大小
    
    Args:
        file: 上传的文件
        max_size: 最大文件大小（字节）
    
    Returns:
        bool: 文件大小是否有效
    """
    if hasattr(file, 'size') and file.size:
        return file.size <= max_size
    return True  # 如果无法获取文件大小，则跳过验证

async def save_upload_file(file: UploadFile, directory: str, filename: str) -> str:
    """
    保存上传的文件
    
    Args:
        file: 上传的文件
        directory: 保存目录
        filename: 文件名
    
    Returns:
        str: 保存的文件路径
    """
    # 确保目录存在
    os.makedirs(directory, exist_ok=True)
    
    file_path = os.path.join(directory, filename)
    
    async with aiofiles.open(file_path, 'wb') as f:
        content = await file.read()
        await f.write(content)
    
    return file_path

def create_thumbnail(image_path: str, thumbnail_path: str, size: Tuple[int, int] = (300, 300)) -> bool:
    """
    创建图片缩略图
    
    Args:
        image_path: 原图片路径
        thumbnail_path: 缩略图保存路径
        size: 缩略图尺寸
    
    Returns:
        bool: 是否成功创建缩略图
    """
    try:
        with Image.open(image_path) as img:
            # 转换为RGB模式（处理RGBA等格式）
            if img.mode in ('RGBA', 'LA', 'P'):
                img = img.convert('RGB')
            
            # 创建缩略图
            img.thumbnail(size, Image.Resampling.LANCZOS)
            
            # 确保缩略图目录存在
            os.makedirs(os.path.dirname(thumbnail_path), exist_ok=True)
            
            # 保存缩略图
            img.save(thumbnail_path, 'JPEG', quality=85)
            return True
    except Exception as e:
        print(f"创建缩略图失败: {e}")
        return False

def get_file_hash(file_path: str) -> str:
    """
    计算文件的MD5哈希值
    
    Args:
        file_path: 文件路径
    
    Returns:
        str: 文件的MD5哈希值
    """
    hash_md5 = hashlib.md5()
    with open(file_path, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()

def get_file_url(file_path: str) -> str:
    """
    将本地文件路径转换为URL
    
    Args:
        file_path: 本地文件路径
    
    Returns:
        str: 文件访问URL
    """
    # 移除上传目录前缀，生成相对路径
    relative_path = os.path.relpath(file_path, settings.upload_dir)
    # 转换为URL格式（使用正斜杠）
    url_path = relative_path.replace(os.sep, '/')
    return f"/uploads/{url_path}"

async def process_image_upload(file: UploadFile, upload_type: str = "work") -> dict:
    """
    处理图片上传
    
    Args:
        file: 上传的图片文件
        upload_type: 上传类型（avatar/work）
    
    Returns:
        dict: 包含文件信息的字典
    
    Raises:
        HTTPException: 如果文件验证失败
    """
    # 验证文件类型
    if not validate_file_type(file, settings.allowed_image_types):
        raise HTTPException(status_code=400, detail="不支持的图片格式")
    
    # 验证文件大小
    if not validate_file_size(file, settings.max_file_size):
        raise HTTPException(status_code=413, detail="文件大小超出限制")
    
    # 生成文件名
    filename = generate_filename(file.filename, upload_type)
    
    # 确定保存目录
    if upload_type == "avatar":
        save_dir = os.path.join(settings.upload_dir, "avatars")
    else:
        save_dir = os.path.join(settings.upload_dir, "works")
    
    # 保存文件
    file_path = await save_upload_file(file, save_dir, filename)
    
    # 创建缩略图
    thumbnail_filename = f"thumb_{filename}"
    thumbnail_dir = os.path.join(settings.upload_dir, "thumbnails")
    thumbnail_path = os.path.join(thumbnail_dir, thumbnail_filename)
    
    thumbnail_created = create_thumbnail(file_path, thumbnail_path)
    
    # 获取文件信息
    file_size = os.path.getsize(file_path)
    file_url = get_file_url(file_path)
    thumbnail_url = get_file_url(thumbnail_path) if thumbnail_created else None
    
    return {
        "url": file_url,
        "thumbnail": thumbnail_url,
        "size": file_size,
        "mime_type": file.content_type,
        "filename": filename
    }

def cleanup_file(file_path: str) -> bool:
    """
    清理文件
    
    Args:
        file_path: 要删除的文件路径
    
    Returns:
        bool: 是否成功删除
    """
    try:
        if os.path.exists(file_path):
            os.remove(file_path)
            return True
        return False
    except Exception as e:
        print(f"删除文件失败: {e}")
        return False

def format_file_size(size_bytes: int) -> str:
    """
    格式化文件大小
    
    Args:
        size_bytes: 文件大小（字节）
    
    Returns:
        str: 格式化后的文件大小
    """
    if size_bytes == 0:
        return "0B"
    
    size_names = ["B", "KB", "MB", "GB", "TB"]
    i = 0
    while size_bytes >= 1024 and i < len(size_names) - 1:
        size_bytes /= 1024.0
        i += 1
    
    return f"{size_bytes:.1f}{size_names[i]}"